﻿using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.Events;

namespace GameCore
{
    /// <summary>
    /// 循环动态容器
    /// @author:dingjie
    /// </summary>
    [DisallowMultipleComponent]
    public class UIDynamic : MonoBehaviour
    {
        /// <summary>
        /// 刷新回调
        /// </summary>
        public UnityAction<GameObject, int> UpdateItem;
        /// <summary>
        /// 回收回调
        /// </summary>
        public UnityAction<GameObject, int> CollectItem;
        public enum Arrangement
        {
            Horizontal,
            Vertical,
        }

        /// <summary>
        /// 布局方式
        /// </summary>
        public Arrangement arrangement = Arrangement.Horizontal;
        /// <summary>
        /// 每行最大数量
        /// horizontal： 每列数量
        /// vertical： 每行数量
        /// </summary>
        public int maxPerLine = 1;
        /// <summary>
        /// 子物体宽度
        /// </summary>
        public float cellWidth = 200f;
        /// <summary>
        /// 子物体高度
        /// </summary>
        public float cellHeight = 200f;
        /// <summary>
        /// 物体之间的横向间隔间隙
        /// </summary>
        public float widthSpace = 0f;
        /// <summary>
        /// 子物体之间的纵向间隔
        /// </summary>
        public float hegithSpace = 0f;
        /// <summary>
        /// horizontal:每行可视范围内的物体数量
        /// vertical:每列可视范围内的物体数量
        /// </summary>
        public int viewCount = 5;
        public ScrollRect scrollRect;
        /// <summary>
        /// 子物体挂载点
        /// </summary>
        public RectTransform content;
        /// <summary>
        /// 预制体
        /// </summary>
        public GameObject goItemPrefab;
        /// <summary>
        /// 数据数量
        /// </summary>
        private int dataCount;
        private int curScrollPerLineIndex = -1;
        /// <summary>
        /// 当前使用列表
        /// </summary>
        private List<UIDynamicItem> listItem = new List<UIDynamicItem>();
        /// <summary>
        /// 回收列表
        /// </summary>
        private Queue<UIDynamicItem> unUseItem = new Queue<UIDynamicItem>();
        /// <summary>
        /// The scroll rect trans.
        /// </summary>
        private RectTransform _scrollRectTrans;


        private float max;
        void Awake()
        {
            scrollRect.onValueChanged.AddListener(OnValueChanged);
            //如果是这样的预制体，直接丢到看不见的位置去
            if (goItemPrefab != null && goItemPrefab.transform.IsChildOf(this.transform))
                this.goItemPrefab.transform.position = GetLocalPositionByIndex(-1);
        }

        public List<UIDynamicItem> useList
        { get { return listItem; } }

        public RectTransform scrollRectTrans
        {
            get
            {
                if (_scrollRectTrans == null)
                {
                    _scrollRectTrans = scrollRect.GetComponent<RectTransform>();
                }

                return _scrollRectTrans;
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="dataCount">Data count.</param>
        public void Init(int dataCount)
        {
            if (scrollRect == null || content == null || goItemPrefab == null)
            {
                Logger.LogError((object)("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab));
                return;
            }
            //第一次进来
            if (curScrollPerLineIndex == -1)
            {
                this.dataCount = dataCount;
                SetUpdateContentSize();
                SetUpdateRectItem(0);
            }
            else if (dataCount < this.dataCount)
            {
                this.dataCount = dataCount;
                SetUpdateContentSize();
                SetUpdateRectItem(GetCurScrollPerLineIndex(), true);
            }
            else
            {
                this.dataCount = dataCount;
                SetUpdateContentSize();
                SetUpdateRectItem(curScrollPerLineIndex, true);
            }
        }

        /// <summary>
        /// 跳到指定位置
        /// </summary>
        /// <param name="index"></param>
        public void JumpToIndex(int index) {
            float value = Mathf.Clamp01(index / dataCount);
            if (arrangement == Arrangement.Horizontal)
            {
                scrollRect.horizontalNormalizedPosition=value;
            }
            else {
                scrollRect.verticalNormalizedPosition =1- value;
            }
        }

        /// <summary>
        /// 滚动
        /// </summary>
        /// <param name="vt2">Vt2.</param>
        private void OnValueChanged(Vector2 vt2)
        {
            int _curScrollPerLineIndex = GetCurScrollPerLineIndex();
            if (_curScrollPerLineIndex == curScrollPerLineIndex)
            {
                return;
            }
            SetUpdateRectItem(_curScrollPerLineIndex);
        }

        /// <summary>
        /// 刷新区域内的ITEM
        /// </summary>
        /// <param name="scrollPerLineIndex">Scroll per line index.</param>
        /// <param name="forceUpdate">If set to <c>true</c> force update.</param>
        private void SetUpdateRectItem(int scrollPerLineIndex, bool forceUpdate = false)
        {

            curScrollPerLineIndex = scrollPerLineIndex;
            int startDataIndex = curScrollPerLineIndex * maxPerLine;
            int endDataIndex = (curScrollPerLineIndex + viewCount) * maxPerLine;
            endDataIndex = Mathf.Min(endDataIndex, dataCount);
            for (int i = listItem.Count - 1; i >= 0; i--)
            {
                UIDynamicItem item = listItem[i];
                int index = item.Index;
                if (index < startDataIndex || index >= endDataIndex)
                {
                    CollectOne(item);
                }
            }
            //显示
            UIDynamicItem tmpItem = null;
            for (int dataIndex = startDataIndex; dataIndex < endDataIndex; dataIndex++)
            {
                if (dataIndex >= dataCount)
                {
                    continue;
                }
                if (IsExistDataByDataIndex(dataIndex, out tmpItem))
                {
                    if (forceUpdate)
                    {
                        tmpItem.transform.localPosition = GetLocalPositionByIndex(dataIndex);
                        CallUpdateItem(tmpItem);
                    }
                    continue;
                }
                CreateItem(dataIndex);
            }
        }

        /// <summary>
        /// 回收一个
        /// </summary>
        private void CollectOne(UIDynamicItem item)
        {
            item.Index = -1;
            item.name = "collectItem";
            listItem.Remove(item);
            unUseItem.Enqueue(item);
            item.transform.localPosition = CollectPosition;
           // item.gameObject.SetActive(false);
            CallCollectItem(item);
        }

        private Vector3 CollectPosition {
            get {
                return new Vector3(-cellWidth-widthSpace,cellHeight+ hegithSpace);
            }
        }


        /// <summary>
        /// 获取当前index下对应Content下的本地坐标
        /// </summary>
        /// <returns>The local position by index.</returns>
        /// <param name="index">Index.</param>
        private Vector3 GetLocalPositionByIndex(int index)
        {
            float x = 0f;
            float y = 0f;
            float z = 0f;
            switch (arrangement)
            {
                case Arrangement.Horizontal: //水平方向
                    x = (index / maxPerLine) * (cellWidth + widthSpace);
                    y = -(index % maxPerLine) * (cellHeight + hegithSpace);
                    break;
                case Arrangement.Vertical://垂着方向
                    x = (index % maxPerLine) * (cellWidth + widthSpace);
                    y = -(index / maxPerLine) * (cellHeight + hegithSpace);
                    break;
            }
            return new Vector3(x, y, z);
        }

        /// <summary>
        /// 创建元素
        /// </summary>
        /// <param name="dataIndex">Data index.</param>
        private void CreateItem(int dataIndex)
        {
            UIDynamicItem item;
            if (unUseItem.Count > 0)
            {
                item = unUseItem.Dequeue();
            }
            else
            {
                item = AddChild(goItemPrefab, content).AddComponent<UIDynamicItem>();
            }
            item.Index = dataIndex;
            item.transform.localPosition = GetLocalPositionByIndex(item.Index);
            item.name = dataIndex.ToString("D5");
          //  item.gameObject.SetActive(true);
            listItem.Add(item);
            CallUpdateItem(item);
        }

        /// <summary>
        /// 刷新回调
        /// </summary>
        /// <param name="item">Item.</param>
        private void CallUpdateItem(UIDynamicItem item)
        {
            if (UpdateItem != null)
                UpdateItem(item.gameObject, item.Index);
        }


        /// <summary>
        /// 回收回调
        /// </summary>
        /// <param name="item">Item.</param>
        private void CallCollectItem(UIDynamicItem item)
        {
            if (CollectItem != null)
                CollectItem(item.gameObject, item.Index);
        }

        /// <summary>
        /// 当前数据是否存在List中
        /// </summary>
        /// <returns><c>true</c> if this instance is exist data by data index the specified dataIndex item; otherwise, <c>false</c>.</returns>
        /// <param name="dataIndex">Data index.</param>
        /// <param name="item">Item.</param>
        private bool IsExistDataByDataIndex(int dataIndex, out UIDynamicItem item)
        {
            item = null;
            if (listItem == null || listItem.Count <= 0)
            {
                return false;
            }
            for (int i = 0; i < listItem.Count; i++)
            {
                if (listItem[i].Index == dataIndex)
                {
                    item = listItem[i];
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 根据Content偏移,计算当前开始显示所在数据列表中的行或列
        /// </summary>
        /// <returns>The current scroll per line index.</returns>
        private int GetCurScrollPerLineIndex()
        {
            int index = 0;
            switch (arrangement)
            {
                case Arrangement.Horizontal: //水平方向
                    index = Mathf.FloorToInt(Mathf.Abs(Mathf.Clamp(content.anchoredPosition.x, max, 0))/ (cellWidth + widthSpace));
                    break;
                case Arrangement.Vertical://垂着方向
                    index = Mathf.FloorToInt(Mathf.Clamp(content.anchoredPosition.y, 0, max) / (cellHeight + hegithSpace));
                    break;
            }
            return index;
        }


        /// <summary>
        /// 设置Content的大小
        /// </summary>
        private void SetUpdateContentSize()
        {
            int lineCount = Mathf.CeilToInt((float)dataCount / maxPerLine);
            switch (arrangement)
            {
                case Arrangement.Horizontal:
                    {

                        Vector2 before = content.sizeDelta;
                        content.sizeDelta = new Vector2(cellWidth * lineCount + widthSpace * Mathf.Max((lineCount - 1), 0),content.sizeDelta.y);
                        float maxX = Mathf.Max(content.sizeDelta.x - scrollRectTrans.sizeDelta.x, 0);
                        //content变小，要调整位置，以防止被吊起
                        if (content.sizeDelta.x < before.x && content.anchoredPosition.x < maxX)
                        {
                            content.anchoredPosition = new Vector2(maxX,content.anchoredPosition.y);
                        }
                        max =  Mathf.Min(0,scrollRectTrans.sizeDelta.x - content.sizeDelta.x);
                    }
                    break;
                case Arrangement.Vertical:
                    {
                        Vector2 before = content.sizeDelta;
                        content.sizeDelta = new Vector2(content.sizeDelta.x, cellHeight * lineCount + hegithSpace * Mathf.Max((lineCount - 1),0));
                        float maxY = Mathf.Max(content.sizeDelta.y - scrollRectTrans.sizeDelta.y,0);
                        //content变小，要调整位置，以防止被吊起
                        if (content.sizeDelta.y < before.y && content.anchoredPosition.y> maxY)
                        {
                            content.anchoredPosition = new Vector2(content.anchoredPosition.x, maxY);
                        }
                        max = Mathf.Max(0,content.sizeDelta.y - scrollRectTrans.sizeDelta.y); 
                    }
                    break;
            }
        }


        /// <summary>
        /// 实例化预制对象
        /// </summary>
        /// <returns>The child.</returns>
        /// <param name="goPrefab">Go prefab.</param>
        /// <param name="parent">Parent.</param>
        private GameObject AddChild(GameObject goPrefab, Transform parent)
        {
            if (goPrefab == null || parent == null)
            {
                Logger.LogError((object)"异常。UIWarpContent.cs addChild(goPrefab = null  || parent = null)");
                return null;
            }
            GameObject goChild = GameObject.Instantiate(goPrefab) as GameObject;
            goChild.layer = parent.gameObject.layer;
            goChild.transform.SetParent(parent, false);
            //预制体轴为居中制作，这里按左上角排版
            (goChild.transform as RectTransform).pivot = new Vector2(0, 1);
            return goChild;
        }

        void OnDestroy()
        {

            scrollRect = null;
            content = null;
            goItemPrefab = null;
            UpdateItem = null;

            listItem.Clear();
            unUseItem.Clear();

            listItem = null;
            unUseItem = null;

        }
    }

}
